{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "toc-hr-collapsed": false
   },
   "source": [
    "# 数据容器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Python 中，有个**数据容器**（Container）的概念。\n",
    "\n",
    "其中包括**字符串**、由 `range()` 函数生成的**等差数列**、**列表**（List）、**元组**（Tuple）、**集合**（Set）、**字典**（Dictionary）。\n",
    "\n",
    "这些容器，各有各的用处。其中又分为*可变*容器（Mutable）和*不可变*容器（Immutable）。可变的有列表、集合、字典；不可变的有字符串、`range()` 生成的等差数列、元组。集合，又分为 *Set* 和 *Frozen Set*；其中，Set 是*可变的*，Frozen Set 是*不可变的*。\n",
    "\n",
    "字符串、由 `range()` 函数生成的等差数列、列表、元组是**有序类型**（Sequence Type），而集合与字典是*无序*的。\n",
    "\n",
    "![](images/python-containers-final.png)\n",
    "\n",
    "另外，集合没有*重合*元素。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 迭代（Iterate）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数据容器里的元素是可以被**迭代的**（Iterable），它们其中包含的元素，可以被逐个访问，以便被处理。\n",
    "\n",
    "对于数据容器，有一个操作符，`in`，用来判断某个元素是否属于某个容器。\n",
    "\n",
    "由于数据容器的可迭代性，再加上这个操作符 `in`，在 Python 语言里写循环格外容易且方便（以字符串这个字符的容器作为例子）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "P\n",
      "y\n",
      "t\n",
      "h\n",
      "o\n",
      "n\n"
     ]
    }
   ],
   "source": [
    "for c in 'Python':\n",
    "  print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Python 出现之前，想要完成这样一个访问字符串中的每一个字符的循环，大抵上应该是这样的（比如 C 语言）：\n",
    "\n",
    "``` C\n",
    "# Written in C\n",
    "char *string;\n",
    "\n",
    "scanf(\"%s\",string); \n",
    "int i=strlen(string);\n",
    "int k = 0;\n",
    "while(k<i){    \n",
    "      printf(\"%c\", string[k]);\n",
    "      k++;    \n",
    "  }\n",
    " ```\n",
    " \n",
    " 在 Python 中，简单的 for 循环，只需要指定一个次数就可以了，因为有 range() 这个函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n",
      "6\n",
      "7\n",
      "8\n",
      "9\n"
     ]
    }
   ],
   "source": [
    "for i in range(10):\n",
    "  print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "即便是用比 C 更为 “现代” 一点的 JavaScript，也大抵上应该是这样的：\n",
    "\n",
    "```c\n",
    "var i;\n",
    "for (i = 0; i < 10; i++) { \n",
    "  console.log(i)\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当然，有时候我们也需要比较复杂的计数器，不过，Python 也不只有 `for` 循环，还有 `while` 循环，在必要的时候可以写复杂的计数器。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "toc-hr-collapsed": true
   },
   "source": [
    "## 列表（List）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "列表和字符串一样，是个*有序类型*（Sequence Type）的容器，其中包含着有索引编号的元素。\n",
    "\n",
    "列表中的元素可以是不同类型。不过，在解决现实问题的时候，我们总是倾向于创建由同一个类型的数据构成的列表。遇到由不同类型数据构成的列表，我们更可能做的是想办法把不同类型的数据分门别类地拆分出来，整理清楚 —— 这种工作甚至有个专门的名称与之关联：*数据清洗*。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 列表的生成"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "生成一个列表，有以下几种方式：\n",
    "\n",
    "\n",
    "```python\n",
    "a_list = []\n",
    "b_list = [1, 2, 3]\n",
    "list(), or list(iterable)            # 这是 Type Casting\n",
    "[(expression with x) for x in iterable]\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2] has a length of 2.\n",
      "[1, 2, 3, 4, 5, 6, 7, 8, 11] has a length of 9.\n",
      "[1, 2, 4, 8, 16, 32, 64, 128] has a length of 8.\n"
     ]
    }
   ],
   "source": [
    "a_list = []\n",
    "a_list.append(1)\n",
    "a_list.append(2)\n",
    "print(a_list, f'has a length of {len(a_list)}.')\n",
    "\n",
    "#range() 返回的不是 list，需要用 list() 转换，否则也没办法调用 .append()\n",
    "b_list = list(range(1, 9))\n",
    "b_list.append(11)\n",
    "print(b_list, f'has a length of {len(b_list)}.')\n",
    "\n",
    "\n",
    "c_list = [2**x for x in range(8)]\n",
    "print(c_list, f'has a length of {len(c_list)}.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这最后一种方式颇为神奇：\n",
    "\n",
    "```python\n",
    "[2**x for x in range(8)]\n",
    "```\n",
    "\n",
    "这种做法，叫做 **[List Comprehension](https://docs.python.org/3.7/tutorial/datastructures.html#tut-listcomps)**。\n",
    "\n",
    "*Comprehend* 这个词的意思除了 “理解” 之外，还有另外一个意思，就是 “包括、囊括” —— 这样的话，你就大概能理解这种做法为什么被称作 *List Comprehension* 了。中文翻译中，怎么翻译的都有，“列表生成器”、“列表生成式” 等等，都挺好。但是，被翻译成 “列表解析器”，就不太好了，给人的感觉是操作反了……\n",
    "\n",
    "List comprehension 可以嵌套使用 `for`，甚至可以加上条件 `if`。官方文档里有个例子，是用来把两个元素并不完全相同的列表去同后拼成一个列表（下面稍作了改写）：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a_list comprehends 10 random numbers: [52, 34, 7, 96, 33, 79, 95, 18, 37, 46]\n",
      "... and it has 5 even numbers: [52, 34, 96, 18, 46]\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "n = 10 \n",
    "\n",
    "# 生成一个 n 个元素的序列，每个元素是 1~100 之间的随机数\n",
    "a_list = [random.randrange(1, 100) for i in range(n)]\n",
    "print(f'a_list comprehends {len(a_list)} random numbers: {a_list}')\n",
    "\n",
    "# 从 a_list 里把偶数都挑出来\n",
    "b_list = [x for x in a_list if x % 2 == 0]\n",
    "print(f'... and it has {len(b_list)} even numbers: {b_list}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 列表的操作符"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "列表的操作符和字符串一样，因为它们都是有序容器。列表的操作符有：\n",
    "\n",
    "> * 拼接：`+`（与字符串不一样的地方是，不能用空格 `' '` 了）\n",
    "> * 复制：`*`\n",
    "> * 逻辑运算：`in` 和 `not in`，`<`、`<=`、`>`、`>=`、`!=`、`==`\n",
    "\n",
    "而后两个列表也和两个字符串一样，可以被比较，即，可以进行逻辑运算；比较方式也跟字符串一样，从两个列表各自的第一个元素开始逐个比较，“一旦决出胜负马上停止”："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 4, 5, 6, 4, 5, 6, 4, 5, 6]"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "a_list = [1, 2, 3]\n",
    "b_list = [4, 5, 6]\n",
    "c_list = a_list + b_list * 3\n",
    "c_list\n",
    "7 not in c_list\n",
    "a_list > b_list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 根据索引提取列表元素"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "列表当然也可以根据索引操作，但由于列表是可变序列，所以，不仅可以提取，还可以删除，甚至替换。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[77, 66, 79]\n",
      "[77, 66, 79, 'L', 'Z', 'R', 77, 66, 79, 77, 66, 79]\n",
      "\n",
      "L\n",
      "[77, 66, 79, 'L', 'Z', 'R', 77, 66, 79, 77, 66, 79]\n",
      "['R', 77, 66, 79, 77, 66, 79]\n",
      "[77, 66, 79]\n",
      "[79, 'L', 'Z', 'R']\n",
      "\n",
      "[77, 66, 79, 'Z', 'R', 77, 66, 79, 77, 66, 79]\n",
      "[77, 66, 79, 'Z', 'R', 77, 66, 79]\n",
      "\n",
      "[77, 'a', 79, 2, 'R', 77, 66, 79]\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "n = 3 \n",
    "a_list = [random.randrange(65, 91) for i in range(n)]\n",
    "b_list = [chr(random.randrange(65, 91)) for i in range(n)]\n",
    "print(a_list)\n",
    "c_list = a_list + b_list + a_list * 2\n",
    "print(c_list)\n",
    "\n",
    "print()\n",
    "# 根据索引提取（Slicing）\n",
    "print(c_list[3])        # 返回索引值为 3 的元素值\n",
    "print(c_list[:])        # 相当于 c_list，返回整个列表\n",
    "print(c_list[5:])       # 从索引为 5 的值开始直到末尾\n",
    "print(c_list[:3])       # 从索引 0 开始，直到索引 3 之前（不包括 3）\n",
    "print(c_list[2:6])      # 从索引 2 开始，直到索引 6 之前（不包括 6）\n",
    "\n",
    "print()\n",
    "# 根据索引删除\n",
    "del c_list[3]\n",
    "print(c_list)           # del 是个命令，del c_list[3] 是一个语句；不能这么写：print(del c_list[3])\n",
    "del c_list[5:8]         \n",
    "print(c_list)\n",
    "\n",
    "print()\n",
    "# 根据索引替换\n",
    "c_list[1:5:2] = ['a', 2]  # s[start:stop:step] = t，跟 range 的三个参数类似；\n",
    "                         # len(t) = len([start:stop:step]) 必须为真\n",
    "print(c_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "需要注意的地方是：**列表**（List）是可变序列，而**字符串**（str）是不可变序列，所以，对字符串来说，虽然也可以根据索引提取，但没办法根据索引删除或者替换。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tho\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "'str' object doesn't support item deletion",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-7-c9c999709965>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'Python'\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mdel\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m  \u001b[0;31m# 这一句会报错\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: 'str' object doesn't support item deletion"
     ]
    }
   ],
   "source": [
    "s = 'Python'[2:5]\n",
    "print(s)\n",
    "del s[2]  # 这一句会报错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "之前提到过：\n",
    "\n",
    "> 字符串常量（String Literal）是不可变有序容器，所以，虽然字符串也有一些 Methods 可用，但那些 Methods 都不改变它们自身，而是在操作后返回一个值给另外一个变量。\n",
    "\n",
    "而对于列表这种*可变容器*，我们可以对它进行操作，结果是*它本身被改变*了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Python\n",
      "['P', 'y', 't', 'h', 'o', 'n']\n",
      "['P', 'y', 'h', 'o', 'n']\n"
     ]
    }
   ],
   "source": [
    "s = 'Python'\n",
    "L = list(s)\n",
    "print(s)\n",
    "print(L)\n",
    "del L[2]\n",
    "print(L) # 用 del 对 L 操作之后，L 本身少了 1 个元素"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 列表可用的内建函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "列表和字符串都是容器，它们可使用的内建函数也其实都是一样的：\n",
    "\n",
    "> * `len()`\n",
    "> * `max()`\n",
    "> * `min()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[89, 84, 85]\n",
      "['X', 'B', 'X']\n",
      "[89, 84, 85, 'X', 'B', 'X', 89, 84, 85, 89, 84, 85]\n",
      "[89, 84, 85, 89, 84, 85, 89, 84, 85]\n",
      "12\n",
      "X\n",
      "B\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "n = 3 \n",
    "\n",
    "# 生成 3 个随机数，构成一个列表\n",
    "a_list = [random.randrange(65, 91) for i in range(n)]\n",
    "b_list = [chr(random.randrange(65, 91)) for i in range(n)]\n",
    "print(a_list)\n",
    "print(b_list)\n",
    "\n",
    "# 列表可以使用操作符 + 和*\n",
    "c_list = a_list + b_list + a_list * 2\n",
    "print(c_list)\n",
    "\n",
    "a_list *= 3\n",
    "print(a_list)\n",
    "\n",
    "# 内建函数操作 len()、max()、min()\n",
    "print(len(c_list))\n",
    "print(max(b_list)) # 内建函数内部做了异常处理，可以在字符之间或者数字之间进行比较 —— 初学者最讨厌这种事情了……\n",
    "print(min(b_list)) # 注意，max() 和 min() 应用的是 b_list, len() 应用的是 c_list —— 请自行找到对应的 list 进行分析。\n",
    "\n",
    "print('X' not in b_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Methods"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "字符串常量和 range() 都是不可变的（Immutable）；而列表则是**可变类型**（Mutable type），所以，它最起码可以被排序 —— 使用 `sort()` Method："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a_list comprehends 10 random numbers:\n",
      " [98, 9, 95, 15, 80, 70, 98, 82, 88, 46]\n",
      "the list sorted:\n",
      " [9, 15, 46, 70, 80, 82, 88, 95, 98, 98]\n",
      "the list sorted reversely:\n",
      " [98, 98, 95, 88, 82, 80, 70, 46, 15, 9]\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "n = 10 \n",
    "a_list = [random.randrange(1, 100) for i in range(n)]\n",
    "print(f'a_list comprehends {len(a_list)} random numbers:\\n', a_list)\n",
    "\n",
    "a_list.sort()\n",
    "print('the list sorted:\\n', a_list)\n",
    "\n",
    "a_list.sort(reverse=True) #reverse 参数，默认是 False\n",
    "print('the list sorted reversely:\\n', a_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果列表中的元素全都是由字符串构成的，当然也可以排序："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a_list comprehends 10 random string elements:\n",
      " ['B', 'U', 'H', 'D', 'C', 'V', 'V', 'Q', 'U', 'P']\n",
      "the list sorted:\n",
      " ['B', 'C', 'D', 'H', 'P', 'Q', 'U', 'U', 'V', 'V']\n",
      "the list sorted reversely:\n",
      " ['V', 'V', 'U', 'U', 'Q', 'P', 'H', 'D', 'C', 'B']\n",
      "\n",
      "b_list comprehends 10 random string elements:\n",
      " ['Nl', 'Mh', 'Ta', 'By', 'Ul', 'Nc', 'Gu', 'Rp', 'Pv', 'Bu']\n",
      "the sorted:\n",
      " ['Bu', 'By', 'Gu', 'Mh', 'Nc', 'Nl', 'Pv', 'Rp', 'Ta', 'Ul']\n",
      "the sorted reversely:\n",
      " ['Ul', 'Ta', 'Rp', 'Pv', 'Nl', 'Nc', 'Mh', 'Gu', 'By', 'Bu']\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "n = 10 \n",
    "\n",
    "a_list = [chr(random.randrange(65, 91)) for i in range(n)]\n",
    "# chr() 函数会返回指定 ascii 码的字符，ord('A') 是 65\n",
    "print(f'a_list comprehends {len(a_list)} random string elements:\\n', a_list)\n",
    "\n",
    "a_list.sort()\n",
    "print('the list sorted:\\n', a_list)\n",
    "\n",
    "a_list.sort(reverse=True) #reverse 参数，默认是 False\n",
    "print('the list sorted reversely:\\n', a_list)\n",
    "\n",
    "print()\n",
    "\n",
    "b_list = [chr(random.randrange(65, 91)) +\\\n",
    "            chr(random.randrange(97, 123))\\\n",
    "            for i in range(n)]\n",
    "# 可以在行末加上 \\ 符号，表示 “该行未完待续……”\n",
    "\n",
    "print(f'b_list comprehends {len(b_list)} random string elements:\\n', b_list)\n",
    "\n",
    "b_list.sort()\n",
    "print('the sorted:\\n', b_list)\n",
    "\n",
    "b_list.sort(key=str.lower, reverse=True) \n",
    "# key 参数，默认是 None\n",
    "# key=str.lower 的意思是，在比较的时候，先全都转换成小写再比较……\n",
    "# —— 但并不改变原有值\n",
    "print('the sorted reversely:\\n', b_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**注意**：不能乱比较…… 被比较的元素应该是同一类型 —— 所以，不是由同一种数据类型元素构成的列表，不能使用 `sort()` Method。下面的代码会报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'<' not supported between instances of 'str' and 'int'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-12-acb9480a455d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0ma_list\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'a'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'c'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0ma_list\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0ma_list\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msort\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 这一句会报错\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: '<' not supported between instances of 'str' and 'int'"
     ]
    }
   ],
   "source": [
    "a_list = [1, 'a', 'c']\n",
    "a_list = a_list.sort() # 这一句会报错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**可变序列**还有一系列可用的 **Methods**：`a.append()`，`a.clear()`，`a.copy()`，`a.extend(t)`，`a.insert(i，x)`，`a.pop([i])`，`a.remove(x)`，`a.reverse()`……"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[90, 88, 73]\n",
      "[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73]\n",
      "[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']\n",
      "\n",
      "[90, 88, 73]\n",
      "[]\n",
      "\n",
      "[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']\n",
      "[90, 88, 73, 'T', 'N', 'Y', 73, 90, 88, 73, '100']\n",
      "[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']\n",
      "\n",
      "[90, 88, 73, 'T', 'N', 'Y', 88, 73, '100']\n",
      "[90, 88, 73, 'T', 'N', 'Y', 88, 73, '100']\n",
      "\n",
      "[]\n",
      "[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']\n",
      "\n",
      "[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']\n",
      "[90, 'example', 88, 'example', 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']\n",
      "\n",
      "[90, 'example', 88, 'example', 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']\n",
      "['100', 73, 88, 90, 73, 88, 90, 'Y', 'N', 'T', 73, 'example', 88, 'example', 90]\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "n = 3 \n",
    "a_list = [random.randrange(65, 91) for i in range(n)]\n",
    "b_list = [chr(random.randrange(65, 91)) for i in range(n)]\n",
    "print(a_list)\n",
    "c_list = a_list + b_list + a_list * 2\n",
    "print(c_list)\n",
    "\n",
    "# 在末尾追加一个元素\n",
    "c_list.append('100')\n",
    "print(c_list)\n",
    "\n",
    "# 清空序列\n",
    "print()\n",
    "print(a_list)\n",
    "a_list.clear()\n",
    "print(a_list)\n",
    "\n",
    "print()\n",
    "# 拷贝一个列表\n",
    "d_list = c_list.copy()\n",
    "print(d_list)\n",
    "del d_list[6:8]\n",
    "print(d_list)\n",
    "print(c_list)             # 对一个拷贝操作，不会更改 “原件”\n",
    "\n",
    "print()\n",
    "# 演示拷贝 .copy() 与赋值 = 的不同\n",
    "e_list = d_list\n",
    "del e_list[6:8]\n",
    "print(e_list)\n",
    "print(d_list)             # 对 e_list 操作，相等于对 d_list 操作\n",
    "\n",
    "# 在末尾追加一个列表\n",
    "print()\n",
    "print(a_list)\n",
    "a_list.extend(c_list)      # 相当于 a_list += c_list\n",
    "print(a_list)\n",
    "\n",
    "# 在某索引位置插入一个元素\n",
    "print()\n",
    "print(a_list)\n",
    "a_list.insert(1, 'example')   # 在索引 1 的位置插入 'example'\n",
    "a_list.insert(3, 'example')   # 在索引 3 的位置插入 'example'；\n",
    "print(a_list)\n",
    "\n",
    "# 排序\n",
    "\n",
    "# a_list.sort() 这一句会出错，因为当前列表中的元素，是 int 和 str 混合的。\n",
    "\n",
    "print()\n",
    "print(a_list)\n",
    "a_list.reverse()\n",
    "print(a_list)\n",
    "x = a_list.reverse() # reverse() 只对当前序列操作，并不返回一个逆序列表；返回值是 None\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有一个命令、两个 Methods 与删除单个元素相关联，`del`，`a.pop([i])`，`a.remove(x)`，请注意它们之间的区别。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[88, 84, 69]\n",
      "\n",
      "\n",
      "[88, 'example', 84, 69]\n",
      "[88, 84, 69]\n",
      "\n",
      "[88, 84, 69]\n",
      "[88, 84]\n",
      "69\n",
      "\n",
      "[88, 84, 'example', 'example']\n",
      "[88, 84, 'example']\n",
      "\n",
      "None\n",
      "[88, 84]\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "n = 3 \n",
    "a_list = [random.randrange(65, 91) for i in range(n)]\n",
    "print(a_list)\n",
    "\n",
    "# 插入\n",
    "print()\n",
    "a_list.insert(1, 'example')   # 在索引 1 的位置插入 'example'\n",
    "\n",
    "# 删除\n",
    "print()\n",
    "print(a_list)\n",
    "a_list.remove('example')      # 去除 'example' 这个元素，如果有多个 'example'，只删除第一个\n",
    "print(a_list)\n",
    "\n",
    "# pop() 删除并返回被删除的值\n",
    "\n",
    "print()\n",
    "print(a_list)\n",
    "p = a_list.pop(2)      # 去除索引为 2 的元素，且返回元素的值，赋值给 p\n",
    "print(a_list)\n",
    "print(p)\n",
    "\n",
    "# pop() 与 del，或者 remove() 的区别\n",
    "print()\n",
    "a_list.insert(2, 'example')\n",
    "a_list.insert(2, 'example')\n",
    "print(a_list)\n",
    "del a_list[2]\n",
    "print(a_list)\n",
    "\n",
    "print()\n",
    "print(a_list.remove('example')) # a_list.remove() 这个 Method 的返回值是 None\n",
    "print(a_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "看起来是个新概念，例子全部读完也很是要花上一段时间，然而，从操作上来看，操作列表和操作字符串的差异并不大，重点在于一个是 Immutable，另外一个是 Mutable，所以，例如像 `a.sort()`，`a.remove()` 这样的事，列表能做，字符串不能做 —— 字符串也可以排序，但那是排序之后返回给另外一个变量；而列表可以直接改变自身……\n",
    "\n",
    "而整理成表格之后呢，理解与记忆真的是零压力：\n",
    "\n",
    "![](images/list-concepts.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 元组（Tuple）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在完整掌握列表的创建与操作之后，再理解元组（Tuple）就容易了，因为它们之间的主要区别只有两个：\n",
    "\n",
    "> * List 是*可变*有序容器，Tuple 是*不可变*有序容器。\n",
    "> * List 用*方括号*标识 `[]`，Tuple 用*圆括号* 标识 `()`。\n",
    "\n",
    "创建一个元组的时候，用圆括号：\n",
    "\n",
    "```python\n",
    "a = ()\n",
    "``` \n",
    "\n",
    "这样就创建了一个空元组。\n",
    "\n",
    "多个元素之间，用 `,` 分离。\n",
    "\n",
    "创建一个含多个元素的元组，可以省略这个括号。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3)\n",
      "(1, 2, 3)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 1, 2, 3   # 不建议这种写法\n",
    "b = (1, 2, 3) # 在创建元组的时候建议永远不省略圆括号……\n",
    "print(a)\n",
    "print(b)\n",
    "a == b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**注意**：创建单个元素的元组，无论是否使用圆括号，在那唯一的元素后面一定要*补上一个逗号* `,`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2,)"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "int"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(2,)"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "a = 2, # 注意这个末尾的逗号 , 它使得 a 变量被定义为一个元组，而不是数字\n",
    "a\n",
    "\n",
    "b = 2  # 整数，赋值\n",
    "b\n",
    "\n",
    "c = (2) # 不是元组\n",
    "c\n",
    "type(c) # 还是 int\n",
    "\n",
    "d = (2,) # 这才是元组\n",
    "d\n",
    "a == d"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "元组是不可变序列，所以，你没办法从里面删除元素。\n",
    "\n",
    "但是，你可以在末尾追加元素。所以，严格意义上，对元组来讲，“不可变” 的意思是说，“**当前已有部分不可变**”……"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1,)\n",
      "4593032496\n",
      "(1, 3, 5)\n",
      "4592468976\n"
     ]
    }
   ],
   "source": [
    "a = 1, \n",
    "print(a)\n",
    "print(id(a))\n",
    "a += 3, 5\n",
    "print(a)\n",
    "print(id(a)) # id 并不相同 —— 实际上是在内存中另外新创建了一个元组……"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "初学者总是很好奇 List 和 Tuple 的区别。首先是使用场景，在将来需要更改的时候，创建 List\n",
    "；在将来不需要更改的时候，创建 Tuple。其次，从计算机的角度来看，Tuple 相对于 List 占用更小的内存。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "48"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "80024"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "90088"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "n = 10000 #@param {type:\"number\"}\n",
    "a = range(n)\n",
    "b = tuple(a) # 把 a 转换成元组\n",
    "c = list(a) # 把 a 转换成列表\n",
    "a.__sizeof__()\n",
    "b.__sizeof__()\n",
    "c.__sizeof__()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "等你了解了 Tuple 的标注方式，你就会发现，`range()` 函数返回的等差数列就是一个 Tuple —— `range(6)` 就相当于 `(0, 1, 2, 3, 4, 5)`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "toc-hr-collapsed": true
   },
   "source": [
    "## 集合（Set）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**集合**（Set）这个容器类型与列表不同的地方在于，首先它*不包含重合元素*，其次它是*无序*的；进而，集合又分为两种，Set，*可变的*，Frozen Set，*不可变的*。\n",
    "\n",
    "创建一个集合，用**花括号** `{}` 把元素括起来，用 `,` 把元素隔开："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{2, 3, 5, 7, 11, 13, 17}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "primes = {2, 3, 5, 7, 11, 13, 17}\n",
    "primes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "toc-hr-collapsed": true
   },
   "source": [
    "### 创建"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意：创建空集合的时候，必须用 `set()`，而不能用 `{}`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "set"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "a = {} # 注意这样创建的是一个 dict（字典），而不是 set 集合\n",
    "b = set() # 这样创建的才是空集合\n",
    "type(a)\n",
    "type(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也可以将序列数据转换（Casting）为集合。转换后，返回的是一个已**去重**的集合。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a', 'b', 'c', 'd', 'e', 'f'}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{1, 2, 3}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'a', 'b', 'e'}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "a = \"abcabcdeabcdbcdef\"\n",
    "b = range(10)\n",
    "c = [1, 2, 2, 3, 3, 1]\n",
    "d = ('a', 'b', 'e', 'b', 'a')\n",
    "set(a)\n",
    "set(b)\n",
    "set(c)\n",
    "set(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Set 当然也可以进行 *Comprehension*："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'d', 'e', 'f'}"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = \"abcabcdeabcdbcdef\"\n",
    "b = {x for x in a if x not in 'abc'}\n",
    "b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "将序列类型数据转换成 Set，就等于**去重**。当然，也可以用 `in` 来判断某个元素是否属于这个集合。`len()`、`max()`、`min()`，也都可以用来操作 Set，但 `del` 却不行 —— 因为 Set 中的元素没有索引（它不是有序容器）。从 Set 里删除元素，得用 `set.remove(elem)`；而 Frozen Set 是不可变的，所以不能用 `set.remove(elem)` 操作。\n",
    "\n",
    "对于集合，有相应的操作符可以对它们进行集合运算：\n",
    "\n",
    "> * 并集：`|`\n",
    "> * 交集：`&`\n",
    "> * 差集：`-`\n",
    "> * 对称差集：`^`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "之前用 `set('abcabcdeabcdbcdef')` 作为简单例子还凑合能用；但这样对读者无意义的集合，无助于进一步的理解。\n",
    "\n",
    "事实上，每种数据结构（Data Structures —— 在这一章里，我们一直用的概念是 “容器”，其实是指同一事物的两种称呼）都有自己的应用场景。比如，当我们需要管理很多用户时，集合就可以派上很大用场。\n",
    "\n",
    "假定两个集合中有些人是 admins，有些人是 moderators：\n",
    "\n",
    "```python\n",
    "admins = {'Moose', 'Joker', 'Joker'}\n",
    "moderators = {'Ann', 'Chris', 'Jane', 'Moose', 'Zero'}\n",
    "```\n",
    "\n",
    "那么："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'Joker', 'Moose'}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'Ann', 'Chris', 'Jane', 'Joker', 'Moose', 'Zero'}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'Moose'}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'Joker'}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'Ann', 'Chris', 'Jane', 'Joker', 'Zero'}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "admins = {'Moose', 'Joker', 'Joker'}\n",
    "moderators = {'Ann', 'Chris', 'Jane', 'Moose', 'Zero'}\n",
    "\n",
    "admins                 # 去重自动完成\n",
    "'Joker' in admins      # Joker 是否是 admins？\n",
    "'Joker' in moderators  # Joker 是否是 moderator？\n",
    "admins | moderators    # admins、moderator，或者身兼两职的，即，两个角色中的所有人 in admins or moderators or both\n",
    "admins & moderators    # 既是 admins 又是 moderator 的都有谁？in both admins and moderators\n",
    "admins - moderators    # 是 admins 但不是 moderator 的都有谁？in admins but not in moderators\n",
    "admins ^ moderators    # admins 和 moderator 中不是身兼两职的都有谁？in admins or moderator but not both"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 这个 cell 集合运算图示需要安装 matplotlib 和 matplotlib-venn\n",
    "# !pip install matplotlib\n",
    "# !pip install matplotlib-venn\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib_venn import venn2\n",
    "\n",
    "admins = {'Moose', 'Joker', 'Joker'}\n",
    "moderators = {'Ann', 'Chris', 'Jane', 'Moose', 'Zero'}\n",
    "\n",
    "v = venn2(subsets=(admins, moderators), set_labels=('admins', 'moderators'))\n",
    "v.get_label_by_id('11').set_text('\\n'.join(admins & moderators))\n",
    "v.get_label_by_id('10').set_text('\\n'.join(admins - moderators))\n",
    "v.get_label_by_id('01').set_text('\\n'.join(moderators - admins))\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上的操作符，都有另外一个版本，即，用 Set 这个类的 Methods 完成。\n",
    "\n",
    "|   意义    | 操作符 | Methods                    | Methods 相当于 |\n",
    "| :------: | :----: | :---------------------: | :--: |\n",
    "| 并集     | <code>&#124;</code>    | `set.union(*others)`         | <code>set &#124; other &#124; ...</code> |\n",
    "| 交集     | `&`    | `set.intersection(*others)` | `set & other & ...` |\n",
    "| 差集     | `-`    | `set.difference(*others)` | `set - other - ...` |\n",
    "| 对称差集 | `^`    | `set.symmetric_difference(other)` | `set ^ other` |\n",
    "\n",
    "注意，并集、交集、差集的 Methods，可以接收多个集合作为参数 `(*other)`，但对称差集 Method 只接收一个参数 `(other)`。\n",
    "\n",
    "对于集合，推荐更多使用 Methods 而不是操作符的主要原因是：更易读 —— 对人来说，因为有意义、有用处的代码终将需要人去维护。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'Chris', 'Jane', 'Joker', 'Moose', 'Zero'}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'Moose'}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'Joker'}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'Chris', 'Jane', 'Joker', 'Zero'}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "admins = {'Moose', 'Joker', 'Joker'}\n",
    "moderators = {'Chris', 'Moose', 'Jane', 'Zero'}\n",
    "\n",
    "admins.union(moderators)\n",
    "admins.intersection(moderators)\n",
    "admins.difference(moderators)\n",
    "admins.symmetric_difference(moderators)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 逻辑运算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "两个集合之间可以进行逻辑比较，返回布尔值。\n",
    "\n",
    "**set** `==` **other**\n",
    "> `True`: set 与 other 相同\n",
    "\n",
    "**set** `!=` **other**\n",
    "> `True`: set 与 other 不同\n",
    "\n",
    "**isdisjoint**(_other_)\n",
    "> `True`: set 与 other 非重合；即，`set & other == None`\n",
    "\n",
    "**issubset**(_other_)，**set** `<=` **other**\n",
    "> `True`: set 是 other 的子集\n",
    "\n",
    "**set** `<` **other**\n",
    "> `True`: set 是 other 的真子集，相当于 `set <= other && set != other`\n",
    "\n",
    "**issuperset**(_other_)，**set** `>=` **other**\n",
    "> `True`: set 是 other 的超集\n",
    "\n",
    "**set** `>` **other**\n",
    "> `True`: set 是 other 的真超集，相当于 `set >= other && set != other`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "toc-hr-collapsed": true
   },
   "source": [
    "### 更新"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于集合，有以下更新它自身的 Method："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**add**(*elem*)\n",
    "> 把 elem 加入集合\n",
    "\n",
    "**remove**(*elem*)\n",
    "> 从集合中删除 elem；如果集合中不包含该 elem，会产生 KeyError 错误。\n",
    "\n",
    "**discard**(*elem*)\n",
    "> 如果该元素存在于集合中，删除它。\n",
    "\n",
    "**pop**(*elem*)\n",
    "> 从集合中删除 elem，并返回 elem 的值，针对空集合做此操作会产生 KeyError 错误。\n",
    "\n",
    "**clear**()\n",
    "从集合中删除所有元素。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**set.update**(*_others_)，相当于 `set |= other | ...`\n",
    "> 更新 set, 加入 others 中的所有元素；\n",
    "\n",
    "**set.intersection_update**(*_others_)，相当于 `set &= other & ...`\n",
    "> 更新 set, 保留同时存在于 set 和所有 others 之中的元素；\n",
    "\n",
    "**set.difference_update**(*_others_)，相当于 `set -= other | ...`\n",
    "> 更新 set, 删除所有在 others 中存在的元素；\n",
    "\n",
    "**set.symmetric_difference_update**(_other_)，相当于 `set ^= other`\n",
    "> 更新 set, 只保留存在于 set 或 other 中的元素，但不保留同时存在于 set 和 other 中的元素；**注意**，该 Method *只接收一个参数*。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 冻结集合"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还有一种集合，叫做冻结集合（Frozen Set），Frozen Set 之于 Set，正如 Tuple 之于 List，前者是不可变容器（Immutable），后者是可变容器（Mutable），无非是为了节省内存使用而设计的类别。\n",
    "\n",
    "有空去看看这个链接就可以了：\n",
    "\n",
    "> https://docs.python.org/3/library/stdtypes.html#frozenset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "toc-hr-collapsed": true
   },
   "source": [
    "## 字典（Dictionary）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Map 是容器中的单独一类，**映射**（Map）容器。映射容器只有一种，叫做**字典**（Dictionary）。先看一个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phonebook = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225}\n",
    "phonebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "字典里的每个元素，由两部分组成，_key_（键）和 _value_（值），二者由一个冒号连接。\n",
    "\n",
    "比如，`'ann':6575` 这个字典元素，_key_ 是 `'ann'`，_value_ 是 `6575`。\n",
    "\n",
    "字典直接使用 _key_ 作为索引，并映射到与它匹配的 _value_："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8982"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "phonebook = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225}\n",
    "phonebook['bob']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在同一个字典里，_key_ 都是唯一的。当创建字典的时候，如果其中有重复的 _key_ 的话，就跟 Set 那样会 “**自动去重**” —— 保留的是众多重复的 _key_ 中的最后一个 _key:value_（或者说，最后一个 _key:value_ “之前那个 _key_ 的 _value_ 被**更新**了”）。字典这个数据类型之所以叫做 Map（映射），是因为字典里的 _key_ 都映射且只映射一个对应的 _value_。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'ann': 6585, 'bob': 8982, 'joe': 2598, 'zoe': 1225}"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "phonebook = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "phonebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在已经了解如何操作列表之后，再去理解字典的操作，其实没什么难度，无非就是字典多了几个 Methods。\n",
    "\n",
    "提蓄一下自己的耐心，把下面的若干行代码都仔细阅读一下，猜一猜输出结果都是什么？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 字典的生成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{}"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'a': 1, 'b': 2, 'c': 3}"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "aDict = {}\n",
    "bDict = {'a':1, 'b':2, 'c':3}\n",
    "aDict\n",
    "bDict"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 更新某个元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2598"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'ann': 6585, 'bob': 8982, 'joe': 5802, 'zoe': 1225}"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "5802"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "\n",
    "phonebook1['joe']\n",
    "phonebook1['joe'] = 5802\n",
    "phonebook1\n",
    "phonebook1['joe']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 添加元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'ann': 6585,\n",
       " 'bob': 8982,\n",
       " 'joe': 2598,\n",
       " 'zoe': 1225,\n",
       " 'john': 9876,\n",
       " 'mike': 5603,\n",
       " 'stan': 6898,\n",
       " 'eric': 7898}"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "phonebook2 = {'john':9876, 'mike':5603, 'stan':6898, 'eric':7898}\n",
    "\n",
    "phonebook1.update(phonebook2)\n",
    "phonebook1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 删除某个元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'bob': 8982, 'joe': 2598, 'zoe': 1225}"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "\n",
    "del phonebook1['ann']\n",
    "phonebook1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 逻辑操作符"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "dict_keys(['ann', 'bob', 'joe', 'zoe'])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "dict_values([6585, 8982, 2598, 1225])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "dict_items([('ann', 6585), ('bob', 8982), ('joe', 2598), ('zoe', 1225)])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "\n",
    "'ann' in phonebook1\n",
    "\n",
    "phonebook1.keys()\n",
    "'stan' in phonebook1.keys()\n",
    "\n",
    "phonebook1.values()\n",
    "1225 in phonebook1.values()\n",
    "\n",
    "phonebook1.items()\n",
    "('stan', 6898) in phonebook1.items()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 可用来操作的内建函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'zoe'"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'ann'"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "['ann', 'bob', 'joe', 'zoe', 'john', 'mike', 'stan', 'eric']"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "('ann', 'bob', 'joe', 'zoe', 'john', 'mike', 'stan', 'eric')"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'ann', 'bob', 'eric', 'joe', 'john', 'mike', 'stan', 'zoe'}"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "['ann', 'bob', 'eric', 'joe', 'john', 'mike', 'stan', 'zoe']"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "['zoe', 'stan', 'mike', 'john', 'joe', 'eric', 'bob', 'ann']"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "phonebook2 = {'john':9876, 'mike':5603, 'stan':6898, 'eric':7898}\n",
    "phonebook1.update(phonebook2)\n",
    "\n",
    "len(phonebook1)\n",
    "max(phonebook1)\n",
    "min(phonebook1)\n",
    "list(phonebook1)\n",
    "tuple(phonebook1)\n",
    "set(phonebook1)\n",
    "sorted(phonebook1)\n",
    "sorted(phonebook1, reverse=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 常用 Methods"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'john': 9876, 'mike': 5603, 'stan': 6898, 'eric': 7898}"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{}"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'john': 9876, 'mike': 5603, 'stan': 6898, 'eric': 7898}"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "('zoe', 1225)"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'ann': 6585, 'bob': 8982, 'joe': 2598}"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "3538"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'ann': 6585, 'bob': 8982, 'joe': 2598}"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "3538"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'ann': 6585, 'bob': 8982, 'joe': 2598}"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "3538"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "{'ann': 6585, 'bob': 8982, 'joe': 2598, 'adam': 3538}"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\"\n",
    "\n",
    "phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "phonebook2 = {'john':9876, 'mike':5603, 'stan':6898, 'eric':7898}\n",
    "\n",
    "phonebook3 = phonebook2.copy()\n",
    "phonebook3\n",
    "\n",
    "phonebook3.clear()\n",
    "phonebook3\n",
    "\n",
    "phonebook2                      # .copy() 的 “原件” 不会发生变化\n",
    "\n",
    "p = phonebook1.popitem()\n",
    "p\n",
    "phonebook1\n",
    "\n",
    "p = phonebook1.pop('adam', 3538)\n",
    "p\n",
    "phonebook1\n",
    "\n",
    "p = phonebook1.get('adam', 3538)\n",
    "p\n",
    "phonebook1\n",
    "\n",
    "p = phonebook1.setdefault('adam', 3538)\n",
    "p\n",
    "phonebook1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "toc-hr-collapsed": true
   },
   "source": [
    "## 迭代各种容器中的元素"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们总是有这样的需求：对容器中的元素逐一进行处理（运算）。这样的时候，我们就用 `for` 循环去迭代它们。\n",
    "\n",
    "对于迭代 `range()` 和 `list` 中的元素我们已经很习惯了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "for i in range(3):\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "for i in [1, 2, 3]:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 迭代的同时获取索引"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有时，我们想同时得到有序容器中的元素及其索引，那么可以调用 `enumerate()` 函数来帮我们："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 P\n",
      "1 y\n",
      "2 t\n",
      "3 h\n",
      "4 o\n",
      "5 n\n"
     ]
    }
   ],
   "source": [
    "s = 'Python'\n",
    "for i, c in enumerate(s):\n",
    "    print(i, c)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 0\n",
      "1 1\n",
      "2 2\n"
     ]
    }
   ],
   "source": [
    "for i, v in enumerate(range(3)):\n",
    "    print(i, v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 ann\n",
      "1 bob\n",
      "2 joe\n",
      "3 john\n",
      "4 mike\n"
     ]
    }
   ],
   "source": [
    "L = ['ann', 'bob', 'joe', 'john', 'mike']\n",
    "for i, L in enumerate(L):\n",
    "    print(i, L)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 ann\n",
      "1 bob\n",
      "2 joe\n",
      "3 john\n",
      "4 mike\n"
     ]
    }
   ],
   "source": [
    "t = ('ann', 'bob', 'joe', 'john', 'mike')\n",
    "for i, t in enumerate(t):\n",
    "    print(i, t)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 迭代前排序"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以用 `sorted()` 和 `reversed()` 在迭代前先排好序："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 ann\n",
      "1 bob\n",
      "2 joe\n",
      "3 john\n",
      "4 mike\n"
     ]
    }
   ],
   "source": [
    "t = ('bob', 'ann', 'john', 'mike', 'joe')\n",
    "for i, t in enumerate(sorted(t)):\n",
    "    print(i, t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 mike\n",
      "1 john\n",
      "2 joe\n",
      "3 bob\n",
      "4 ann\n"
     ]
    }
   ],
   "source": [
    "t = ('bob', 'ann', 'john', 'mike', 'joe')\n",
    "for i, t in enumerate(sorted(t, reverse=True)):\n",
    "    print(i, t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 joe\n",
      "1 mike\n",
      "2 john\n",
      "3 ann\n",
      "4 bob\n"
     ]
    }
   ],
   "source": [
    "t = ('bob', 'ann', 'john', 'mike', 'joe')\n",
    "for i, t in enumerate(reversed(t)):\n",
    "    print(i, t)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 同时迭代多个容器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以在 `zip()` 这个函数的帮助下，同时迭代两个或者两个以上的容器中的元素（这样做的前提是，多个容器中的元素数量最好相同）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Let's assume a represents 1.\n",
      "Let's assume b represents 2.\n",
      "Let's assume c represents 3.\n",
      "Let's assume d represents 4.\n",
      "Let's assume e represents 5.\n",
      "Let's assume f represents 6.\n",
      "Let's assume g represents 7.\n",
      "Let's assume h represents 8.\n",
      "Let's assume i represents 9.\n",
      "Let's assume j represents 10.\n",
      "Let's assume k represents 11.\n",
      "Let's assume l represents 12.\n",
      "Let's assume m represents 13.\n",
      "Let's assume n represents 14.\n",
      "Let's assume o represents 15.\n",
      "Let's assume p represents 16.\n",
      "Let's assume q represents 17.\n",
      "Let's assume r represents 18.\n",
      "Let's assume s represents 19.\n",
      "Let's assume t represents 20.\n",
      "Let's assume u represents 21.\n",
      "Let's assume v represents 22.\n",
      "Let's assume w represents 23.\n",
      "Let's assume x represents 24.\n",
      "Let's assume y represents 25.\n",
      "Let's assume z represents 26.\n"
     ]
    }
   ],
   "source": [
    "chars = 'abcdefghijklmnopqrstuvwxyz'\n",
    "nums = range(1, 27)\n",
    "for c, n in zip(chars, nums):\n",
    "    print(f\"Let's assume {c} represents {n}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 迭代字典中的元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ann 6585\n",
      "bob 8982\n",
      "joe 2598\n",
      "zoe 1225\n"
     ]
    }
   ],
   "source": [
    "phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "\n",
    "for key in phonebook1:\n",
    "    print(key, phonebook1[key])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ann 6585\n",
      "bob 8982\n",
      "joe 2598\n",
      "zoe 1225\n"
     ]
    }
   ],
   "source": [
    "phonebook1 = {'ann':6575, 'bob':8982, 'joe':2598, 'zoe':1225, 'ann':6585}\n",
    "\n",
    "for key, value in phonebook1.items():\n",
    "    print(key, value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这一章的内容，只不过是 “多” 而已，一旦逻辑关系理顺，就会觉得很简单。而这一章的开头，已经是最好的总结了。\n",
    "\n",
    "最后需要补充的，只是两个参考链接，以后有什么搞不明白的地方，去那里翻翻就能找到答案：\n",
    "\n",
    "> * https://docs.python.org/3/tutorial/datastructures.html#dictionaries\n",
    "> * https://docs.python.org/3/library/stdtypes.html#typesmapping"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  },
  "toc-autonumbering": true
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
